package me.xujl;

import java.sql.SQLException;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.camel.Exchange;
import org.apache.camel.RuntimeExchangeException;
import org.apache.camel.component.jdbc.DefaultJdbcPrepareStatementStrategy;
import org.apache.camel.component.jdbc.JdbcConstants;

/**
 *
 * 修改参数组装策略， null值改为也可以传递
 *
 * User: xujl
 * Date: 2014/5/23
 */
public class CustomJdbcPrepareStatementStrategyImpl extends DefaultJdbcPrepareStatementStrategy {

    @Override
    public Iterator<?> createPopulateIterator(final String query, String preparedQuery, int expectedParams, final Exchange exchange, Object value) throws SQLException {
        Map<?, ?> map = null;
        if (exchange.getIn().hasHeaders()) {
            if (exchange.getIn().getHeader(JdbcConstants.JDBC_PARAMETERS) != null) {
                // header JDBC_PARAMETERS takes precedence over regular headers
                map = exchange.getIn().getHeader(JdbcConstants.JDBC_PARAMETERS, Map.class);
            } else {
                map = exchange.getIn().getHeaders();
            }
        }

        final Map<?, ?> headerMap = map;

        if (hasNamedParameters(query)) {
            // create an iterator that returns the value in the named order
            try {

                return new Iterator<Object>() {
                    private NamedQueryParser parser = new NamedQueryParser(query);
                    private boolean done;
                    private Object next;

                    @Override
                    public boolean hasNext() {
                        String nextKey = parser.next();
                        if (nextKey == null) {
                            done = true;
                            return false;
                        }

                        boolean contains = headerMap != null && headerMap.containsKey(nextKey);
                        if (!contains) {
                            throw new RuntimeExchangeException("Cannot find key [" + nextKey + "] in message body or headers to use when setting named parameter in query [" + query + "]", exchange);
                        }
                        next = headerMap.get(nextKey);

                        return true;
                    }

                    @Override
                    public Object next() {
                        if (done) {
                            throw new RuntimeException("Can't find anymore.");
                        }

                        return next;
                    }

                    @Override
                    public void remove() {
                        // noop
                    }
                };
            } catch (Exception e) {
                throw new SQLException("Error iterating parameters for the query: " + query, e);
            }

        } else {
            // just use a regular iterator
            return exchange.getContext().getTypeConverter().convertTo(Iterator.class, headerMap != null ? headerMap.values() : null);
        }

    }

    private static final class NamedQueryParser {

        private static final Pattern PATTERN = Pattern.compile("\\:\\?(\\w+)");
        private final Matcher matcher;

        private NamedQueryParser(String query) {
            this.matcher = PATTERN.matcher(query);
        }

        public String next() {
            if (!matcher.find()) {
                return null;
            }

            return matcher.group(1);
        }
    }

}
